﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using A2DFramework.AOPService.Core;

namespace A2DFramework.InstanceService
{   
    internal static class MapIoCService
    {
        private static List<TypeDefination> mappedTypes = new List<TypeDefination>();
        private static Dictionary<string, object> instances = new Dictionary<string, object>();
        private static object o4lock = new object();

        private static TypeDefination GetTypeDefination(Type key)
        {
            foreach (var t in mappedTypes.AsReadOnly())
                if (t.RequestType.Equals(key))
                    return t;
            return null;
        }

        internal static bool IsTypeKeyExist(Type key)
        {
            var def = GetTypeDefination(key);
            if (def == null)
                return false;
            return true;
        }
        internal static Type GetConcreteType(Type key)
        {
            var def = GetTypeDefination(key);
            return def.ResponseType;
        }

        internal static void RegisterMapping(Type T, Type TProvider, bool singleton)
        {
            var def = GetTypeDefination(T);
            if(def!=null)
                throw new Exception("Key existed");

            lock (o4lock)
            {
                Type t = T;
                TypeDefination defination = new TypeDefination();
                defination.RequestType = t;
                defination.ResponseType = TProvider;
                defination.IsSingleInstance = singleton;
                mappedTypes.Add(defination);
            }
        }
        
        internal static T ResolveInstance<T>()
        {
            return (T)ResolveInstance(typeof(T));
        }
        internal static object ResolveInstance(Type type)
        {
            var def = GetTypeDefination(type);
            if(def==null)
                throw new Exception("Key empty, register please");

            if (def.IsSingleInstance)
                if (instances.ContainsKey(def.Key))
                    return instances[def.Key];

            Type targetType = def.ResponseType;

            int foundInjectFromA2DConstructorsCount = 0;
            ConstructorInfo[] ctrs = targetType.GetConstructors();
            List<object> objList = new List<object>();
            foreach (ConstructorInfo ctr in ctrs)
            {
                ParameterInfo[] ps = ctr.GetParameters();
                foreach (ParameterInfo p in ps)
                {
                    object[] attrs = p.GetCustomAttributes(typeof(InjectFromA2DAttribute), false);
                    if (attrs != null && attrs.Count() > 0)
                        foundInjectFromA2DConstructorsCount += 1;

                    if (attrs != null && attrs.Count() > 0)
                    {
                        object pType = MapIoCService.ResolveInstance(p.ParameterType);
                        objList.Add(pType);
                    }
                }
            }
            if (foundInjectFromA2DConstructorsCount > 1)
                throw new Exception("Cannot more then 1 InjectFromA2DAttribute.");

            object obj = null;
            if (foundInjectFromA2DConstructorsCount > 0)
                obj = Activator.CreateInstance(targetType, objList.ToArray());
            else
                obj = Activator.CreateInstance(targetType);

            if (def.IsSingleInstance)
            {
                if (!instances.ContainsKey(def.Key))
                {
                    instances[def.Key] = obj;
                }
            }

            return obj;
        }
    }
}
